#ifndef DEV_VGA_FUZZER_H_
#define DEV_VGA_FUZZER_H_

#include <VBox/types.h>
#include <VBox/vmm/pdmthread.h>
#include <VBox/vmm/pdmsrv.h>
#include <VBox/vmm/pdmdev.h>
#include <VBoxVideo.h>
#include <DevVGA.h>
#include <DevVGA-SVGA3d.h>
#include <DevVGA-SVGA-internal.h>
#include <DevVGA-SVGA.h>
#include <PDMInternal.h>
#include <fuzzer/FuzzedDataProvider.h>

typedef struct VGADeviceState {
    PDMDEVINSR3 service_instance;
    char padding[sizeof(VGASTATER3)];
} VGADeviceState;

enum class Operation {
    kPIO,
    kCommand,
    kMaxValue
};

extern void ResetVGA(PPDMDEVINS pDevIns);

class DevVGAFuzzer {
    public:
        DevVGAFuzzer() {
           Init();
        }
        ~DevVGAFuzzer() {
         ResetVGA(&device_state_.service_instance);
         ResetCmdBufCtx();
         if (svga_state_->apCmdBufCtxs[0] != nullptr) {
             free(svga_state_->apCmdBufCtxs[0]);
             svga_state_->apCmdBufCtxs[0] = nullptr;
         }

         if (svga_state_->apCmdBufCtxs[1] != nullptr) {
             free(svga_state_->apCmdBufCtxs[1]);
             svga_state_->apCmdBufCtxs[1] = nullptr;
         }

         free(ptr_vga_state_cc_->svga.pau32FIFO);
         free(ptr_vga_state_cc_->pbVRam);
         free(ptr_vga_state_cc_->pDrv);
         free(p3dState_);

         vmsvgaR3Destruct(&device_state_.service_instance);
        }
        void Init();
        void Fuzz(const uint8_t *data, size_t size);
        void InitCmdBufCtx();
        void ResetCmdBufCtx();
        FuzzedDataProvider* GetFdp();
    private:
        PDMTHREAD thread_;
        PDMDEVHLPR3 noop_helper_;
        struct VGAState vga_state_;
        VMSVGAR3STATE *svga_state_;
        VMSVGA3DBACKENDFUNCSGBO *pFuncsGBO_;
        VGADeviceState device_state_;
        PVGASTATER3 ptr_vga_state_cc_;

        PVMSVGA3DSTATE p3dState_;
        // Commands go here.
        VMSVGACMDBUF *cmd_buf_;
        std::unique_ptr<FuzzedDataProvider> fdp_;
};

extern DevVGAFuzzer *fuzzer;
extern std::string *output_filename;

#endif
